﻿#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
/*********************20.⾃定义类型：结构体**********************/
//结构是⼀些值的集合，这些值称为成员变量。结构的每个成员可以是不同类型的变量。
/*1.结构的声明*/

//struct Stu
//{
//	char name[20];
//	int age;
//	float score;
//}s3, s4;//全局变量 
//struct Stu s2; //全局变量
//
//int main()
//{
//	//结构体变量创建和初始化
//	struct Stu s1 = {"zhangsan", 18, 85}; //局部变量
//	struct Stu s2 = { .score = 99, .age = 20, .name = "zhangsan" };
//
//	printf("%s %d %.1f",s2.name, s2.age, s2.score);
//
//	return 0;
//}

/*2.结构体的特殊声明*/

////匿名结构体类型 -- 注定只能使用一次
//struct 
//{
//	int a;
//	char b;
//	double c;
//}s;
//
////匿名结构体指针类型
//struct
//{ 
//	int a;
//	char b;
//	double c;
//}* ps;
//int main()
//{
//	ps = &s; //编译器会把上⾯的两个声明当成完全不同的两个类型，非法
//	return 0;
//}

/*3.结构的⾃引⽤*/
//在结构中包含⼀个类型为该结构本⾝的成员

//struct Node
//{
//	int data;
//	struct Node next;
//};
/*⼀个结构体中再包含⼀个同类型的结构体变量，这样结构体变量的⼤
⼩就会⽆穷的⼤，是不合理的*/

//正确的⾃引⽤⽅式：
//struct Node
//{
//	int data;//数据域
//	struct Node* next;//指针域
//};

/*结构体重命名*/
//typedef struct Node
//{
//	int data;//数据域
//	struct Node* next;//指针域
//}Node, * pNode;
//
////typedef struct Node
////{
////	int data;//数据域
////	struct Node* next;//指针域
////};
////typedef struct Node Node;
////typedef struct Node* pNode;//重命名结构体指针
//
//int main()
//{
//	struct Node n1 = { 1, NULL };
//	Node n2 = { 2, NULL };
//
//	return 0;
//}

/*4.结构内存对⻬*/
//#include <stddef.h>
//struct S1
//{
//	char c1;
//	int i;
//	char c2;
//};
//
//struct S2
//{
//	char c1;
//    char c2;
//	int i;
//};
//
//int main()
//{
//	//printf("%zd\n", sizeof(struct S1));//大小12
//	//printf("%zd\n", sizeof(struct S2));//大小8
//	//大小不一样是因为结构体内存对齐
//
//	//offsetof --计算结构体成员相较于结构体起始位置的偏移量
//	//offsetof是宏，不是函数
//	printf("%zd\n", offsetof(struct S1, c1));
//	printf("%zd\n", offsetof(struct S1, i));
//	printf("%zd\n", offsetof(struct S1, c2));
//
//	return 0;
//}

//struct s3
//{
//	double d;
//	char c;
//	int i;
//};
//
//int main()
//{
//	printf("%d\n", sizeof(struct s3));//16
//	return 0;
//}

//struct S3
//{
//	double d;
//	char c;
//	int i;
//};
//
//struct S4
//{
//	char c1;
//	struct S3 s3;
//	double d;
//};
//
//int main()
//{
//	printf("%d\n", sizeof(struct S4));//32
//	return 0;
//}


/*5.修改默认对⻬数*/
//VS 中默认的对⻬数值为 8

//#pragma pack(1)//设置默认对⻬数为1，就相当于没有对齐数
//struct S
//{
//	char c1;
//	int i;
//	char c2;
//};
//#pragma pack()//取消设置的对⻬数，还原为默认
//
//int main()
//{
//	//输出的结果是什么？
//	printf("%zd\n", sizeof(struct S));//大小6
//	return 0;
//}

/*6. 结构体传参*/
//struct S
//{
//	int n;
//	char data[1000];
//};
//
//void print(struct S t)
//{
//	printf("%d %s\n", t.n, t.data);
//}
//
//void print2(const struct S* pt)
//{
//	printf("%d %s\n", pt->n, pt->data);
//}
//
//int main()
//{
//	struct S s = { 100, "hehe" };
//	print(s); //传值调用
//	print2(&s); //传址调用
//
//	return 0;
//}

/*7. 结构体实现位段*/
/*位段的声明和结构是类似的，有两个不同：
1. 位段的成员必须是 int、unsigned int 或signed int ，在C99中位段成员的类型也可以
选择其他类型。
2. 位段的成员名后边有⼀个冒号和⼀个数字*/

//位段中的位指二进制位
//struct S
//{
//	int n1;
//	int n2;
//};
//
//struct S2
//{
//	int n1 : 10;
//	int n2 : 26;
//};
//
//struct A
//{
//	int _a;//32位
//	int _b;//32位
//	int _c;//32位
//	int _d;//32位
//};
//
//struct A2
//{
//	int _a : 2;//2位
//	int _b : 5;//5位
//	int _c : 10;//10位
//	int _d : 30;//30位
//};
//
//int main()
//{
//	printf("%zd\n",sizeof(struct A));//16字节
//	printf("%zd\n", sizeof(struct A2));//8字节
//
//	return 0;
//}

/*位段的内存分配*/
//1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
//2. 位段的空间上是按照需要以4个字节（ int ）或者1个字节（ char ）的⽅式来开辟的。
//3. 位段涉及很多不确定因素，位段是不跨平台的，注重可移植的程序应该避免使⽤位段。

//struct S
//{
//	char a : 3;
//	char b : 4;
//	char c : 5;
//	char d : 4;
//};
//
//int main()
//{
//	struct S s = { 0 };
//	s.a = 10;
//	s.b = 12;
//	s.c = 3;
//	s.d = 4;
//	printf("%zd",sizeof(struct S));//3
//	return 0;
//}

/*位段使⽤的注意事项*/
/*位段的⼏个成员共有同⼀个字节，这样有些成员的起始位置并不是某个字节的起始位置，那么这些位
置处是没有地址的。内存中每个字节分配⼀个地址，⼀个字节内部的bit位是没有地址的。
所以不能对位段的成员使⽤& 操作符，这样就不能使⽤scanf直接给位段的成员输⼊值，只能是先输⼊
放在⼀个变量中，然后赋值给位段的成员。*/

//struct A
//{
//	int _a : 2;
//	int _b : 5;
//	int _c : 10;
//	int _d : 30;
//};
//
//int main()
//{
//	struct A sa = { 0 };
//	//scanf("%d", &sa._b);//这是错误的
//	//正确的⽰范
//	int b = 0;
//	scanf("%d", &b);
//	sa._b = b;
//	return 0;
//}













